#pragma once

#include "Partitioning.h"

namespace soy {
	// Interface to per-line data that wants to see each line insertion and deletion
	class PerLine {
	public:
		virtual ~PerLine() {}
		virtual void Init() = 0;
		virtual void InsertLine( int ) = 0;
		virtual void RemoveLine( int ) = 0;
	};

/**
 * The line vector contains information about each of the lines in a cell buffer.
 */
class LineVector {

	Partitioning starts;
	PerLine* perLine;

public:

	LineVector();
	~LineVector();
	void Init();
	void SetPerLine( PerLine* pl );

	void InsertText( int line, int delta );
	void InsertLine( int line, int position, bool lineStart );
	void SetLineStart( int line, int position );
	void RemoveLine( int line );
	int Lines() const {
		return starts.Partitions();
	}
	int LineFromPosition( int pos ) const;
	int LineStart( int line ) const {
		return starts.GetValue( line );
	}

	int MarkValue( int line );
	int AddMark( int line, int marker );
	void MergeMarkers( int pos );
	void DeleteMark( int line, int markerNum, bool all );
	void DeleteMarkFromHandle( int markerHandle );
	int LineFromHandle( int markerHandle );

	void ClearLevels();
	int SetLevel( int line, int level );
	int GetLevel( int line );

	int SetLineState( int line, int state );
	int GetLineState( int line );
	int GetMaxLineState();

};

enum actionType { insertAction, removeAction, startAction, containerAction };

/**
 * Actions are used to store all the information required to perform one undo/redo step.
 */
class Action {
public:
	actionType at;
	int position;
	char* data;
	int lenData;
	bool mayCoalesce;

	Action();
	~Action();
	void Create( actionType at_, int position_ = 0, char* data_ = 0, int lenData_ = 0, bool mayCoalesce_ = true );
	void Destroy();
	void Grab( Action* source );
};


/**
 *
 */
class UndoHistory {
	Action* actions;
	int lenActions;
	int maxAction;
	int currentAction;
	int undoSequenceDepth;
	int savePoint;

	void EnsureUndoRoom();

public:
	UndoHistory();
	~UndoHistory();

	void AppendAction( actionType at, int position, char* data, int length, bool& startSequence, bool mayCoalesce = true );

	void BeginUndoAction();
	void EndUndoAction();
	void DropUndoSequence();
	void DeleteUndoHistory();

	/// The save point is a marker in the undo stack where the container has stated that
	/// the buffer was saved. Undo and redo can move over the save point.
	void SetSavePoint();
	bool IsSavePoint() const;

	/// To perform an undo, StartUndo is called to retrieve the number of steps, then UndoStep is
	/// called that many times. Similarly for redo.
	bool CanUndo() const;
	int StartUndo();
	const Action& GetUndoStep() const;
	void CompletedUndoStep();
	bool CanRedo() const;
	int StartRedo();
	const Action& GetRedoStep() const;
	void CompletedRedoStep();
};

/**
 * Holder for an expandable array of characters that supports undo and line markers.
 * Based on article "Data Structures in a Bit-Mapped Text Editor"
 * by Wilfred J. Hansen, Byte January 1987, page 183.
 */
class CellBuffer {
private:
	SplitVector<char> substance;
	SplitVector<char> style;
	bool readOnly;

	bool collectingUndo;
	UndoHistory uh;

	LineVector lv;

	/// Actions without undo
	void BasicInsertString( int position, const char* s, int insertLength );
	void BasicDeleteChars( int position, int deleteLength );

public:

	CellBuffer();
	~CellBuffer();

	/// Retrieving positions outside the range of the buffer works and returns 0
	char CharAt( int position ) const;
	void GetCharRange( char* buffer, int position, int lengthRetrieve ) const;
	char StyleAt( int position ) const;
	void GetStyleRange( unsigned char* buffer, int position, int lengthRetrieve ) const;
	const char* BufferPointer();

	int Length() const;
	void Allocate( int newSize );
	void SetPerLine( PerLine* pl );
	int Lines() const;
	int LineStart( int line ) const;
	int LineFromPosition( int pos ) const {
		return lv.LineFromPosition( pos );
	}
	void InsertLine( int line, int position, bool lineStart );
	void RemoveLine( int line );
	const char* InsertString( int position, const char* s, int insertLength, bool& startSequence );

	/// Setting styles for positions outside the range of the buffer is safe and has no effect.
	/// @return true if the style of a character is changed.
	bool SetStyleAt( int position, char styleValue, char mask = '\377' );
	bool SetStyleFor( int position, int length, char styleValue, char mask );

	const char* DeleteChars( int position, int deleteLength, bool& startSequence );

	bool IsReadOnly() const;
	void SetReadOnly( bool readOnly );

	/// The save point is a marker in the undo stack where the container has stated that
	/// the buffer was saved. Undo and redo can move over the save point.
	void SetSavePoint();
	bool IsSavePoint();

	bool SetUndoCollection( bool collectUndo );
	bool IsCollectingUndo() const;
	void BeginUndoAction();
	void EndUndoAction();
	void AddUndoAction( int token, bool mayCoalesce );
	void DeleteUndoHistory();

	/// To perform an undo, StartUndo is called to retrieve the number of steps, then UndoStep is
	/// called that many times. Similarly for redo.
	bool CanUndo();
	int StartUndo();
	const Action& GetUndoStep() const;
	void PerformUndoStep();
	bool CanRedo();
	int StartRedo();
	const Action& GetRedoStep() const;
	void PerformRedoStep();
};


};
